home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #2 / Monster Media No. 2 (Monster Media)(1994).ISO / bbs_util / ezvote50.zip / EZVOTE.C < prev    next >
C/C++ Source or Header  |  1994-07-23  |  46KB  |  1,307 lines

  1. /*
  2.                 ─────────────────────────────────────────────
  3.                                  EZVote 5.00
  4.                            "The user polling door"
  5.  
  6.                 (C) Copyright 1991, 1992, 1994 by Brian Pirie
  7.                 ─────────────────────────────────────────────
  8.  
  9.  
  10.             FILE : ezvote.c
  11.  
  12.      DESCRIPTION : Source code for EZVote door program. Requires OpenDoors
  13.                    5.00 to compile
  14.  
  15.     MEMORY MODEL : Medium
  16.  
  17.    LAST MODIFIED : July 23, 1994
  18. */
  19.  
  20.  
  21. /* Include standard C header files required by EZVote. */
  22. #include <string.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <time.h>
  26. #include <errno.h>
  27.  
  28. /* Include the OpenDoors header file. This line must be done in any program */
  29. /* using OpenDoors.                                                         */
  30. #include "opendoor.h"
  31.  
  32.  
  33. /* Manifest constants used by EZVote */
  34. #define NO_QUESTION              -1
  35. #define NEW_ANSWER               -1
  36.  
  37. #define QUESTIONS_VOTED_ON        0x0001
  38. #define QUESTIONS_NOT_VOTED_ON    0x0002
  39.  
  40. #define MAX_QUESTIONS             200
  41. #define MAX_USERS                 30000
  42. #define MAX_ANSWERS               15
  43. #define QUESTION_STR_SIZE         71
  44. #define ANSWER_STR_SIZE           31
  45.  
  46. #define USER_FILENAME             "EZVOTE.USR"
  47. #define QUESTION_FILENAME         "EZVOTE.QST"
  48.  
  49. #define FILE_ACCESS_MAX_WAIT      20
  50.  
  51. #define QUESTION_PAGE_SIZE        17
  52.  
  53.  
  54. /* Structure of records stored in the EZVOTE.USR file */
  55. typedef struct
  56. {
  57.    char szUserName[36];
  58.    char bVotedOnQuestion[MAX_QUESTIONS];
  59. } tUserRecord;
  60.               
  61. tUserRecord CurrentUserRecord;
  62. int nCurrentUserNumber;
  63.  
  64.  
  65. /* Structure of records stored in the EZVOTE.QST file */
  66. typedef struct
  67. {
  68.    char szQuestion[QUESTION_STR_SIZE];
  69.    char aszAnswer[MAX_ANSWERS][ANSWER_STR_SIZE];
  70.    int nTotalAnswers;
  71.    unsigned int auVotesForAnswer[MAX_ANSWERS];
  72.    unsigned int uTotalVotes;
  73.    char bCanAddAnswers;
  74.    char bDeleted;
  75.    char szCreatorName[36];
  76.    time_t lCreationTime;
  77. } tQuestionRecord;
  78.  
  79.  
  80. /* Global variables. */
  81. int nViewResultsFrom = QUESTIONS_VOTED_ON;
  82. char bAllowAdd = TRUE;
  83. int nQuestionsVotedOn = 0;
  84.  
  85.  
  86. /* Prototypes for functions that form EZVote */
  87. void CustomConfigFunction(char *pszKeyword, char *pszOptions);
  88. void BeforeExitFunction(void);
  89. void VoteOnQuestion(void);
  90. void ViewResults(void);
  91. int GetQuestion(int nQuestion, tQuestionRecord *pQuestionRecord);
  92. void AddQuestion(void);
  93. void DeleteQuestion(void);
  94. int ChooseQuestion(int nFromWhichQuestions, char *pszTitle, int *nLocation);
  95. void DisplayQuestionResult(tQuestionRecord *pQuestionRecord);
  96. int ReadOrAddCurrentUser(void);
  97. void WriteCurrentUser(void);
  98. FILE *ExculsiveFileOpen(char *pszFileName, char *pszMode);
  99. void WaitForEnter(void);
  100.  
  101.  
  102. /* main() function - Program execution begins here. */
  103. main()
  104. {
  105.    /* Variable to store user's choice from the menu */
  106.    char chMenuChoice;
  107.    char chYesOrNo;
  108.  
  109.    /* Enable use of OpenDoors configuration file system. */
  110.    od_control.od_config_file = INCLUDE_CONFIG_FILE;
  111.  
  112.    /* Set function to process custom configuration file lines. */
  113.    od_control.od_config_function = CustomConfigFunction;
  114.  
  115.    /* Set filename of configuration file. */
  116.    od_control.od_config_filename = "EZVOTE.CFG";
  117.  
  118.    /* Include the OpenDoors multiple personality system, which allows    */
  119.    /* the system operator to set the sysop statusline / function key set */
  120.    /* to mimic the BBS software of their choice.                         */
  121.    od_control.od_mps = INCLUDE_MPS;
  122.    
  123.    /* Include the OpenDoors log file system, which will record when the */
  124.    /* door runs, and major activites that the user performs.            */ 
  125.    od_control.od_logfile = INCLUDE_LOGFILE;
  126.  
  127.    /* Set filename for log file. If not set, DOOR.LOG will be used by */
  128.    /* default.                                                        */
  129.    strcpy(od_control.od_logfile_name, "EZVOTE.LOG");
  130.    
  131.    /* Set program's name, to be written to the OpenDoors log file       */
  132.    strcpy(od_control.od_prog_name, "EZVote 5.00");
  133.  
  134.    /* Set function to be called before program exits. */
  135.    od_control.od_before_exit = BeforeExitFunction;
  136.  
  137.    /* Do not display OpenDoors copyright notice. */
  138.    od_control.od_nocopyright = TRUE;
  139.  
  140.    /* Initialize OpenDoors. This function call is optional, and can be used */
  141.    /* to force OpenDoors to read the door informtion file and begin door    */
  142.    /* operations. If a call to od_init() is not included in your program,   */
  143.    /* OpenDoors initialization will be performed at the time of your first  */
  144.    /* call to any OpenDoors function. */
  145.    od_init();
  146.  
  147.    /* Call the EZVote function ReadOrAddCurrentUser() to read the current */
  148.    /* user's record from the EZVote user file, or to add the user to the  */
  149.    /* file if this is the first time that they have used EZVote.          */
  150.    if(!ReadOrAddCurrentUser())
  151.    {
  152.       /* If unable to obtain a user record for the current user, then exit */
  153.       /* the door after displaying an error message.                       */
  154.       od_printf("Unable to access user file. File may be locked or full.\n\r");
  155.       WaitForEnter();
  156.       od_exit(1, FALSE);
  157.    }   
  158.  
  159.    /* Loop until the user choses to exit the door. For each iteration of  */
  160.    /* this loop, we display the main menu, get the user's choice from the */
  161.    /* menu, and perform the appropriate action for their choice.          */
  162.  
  163.    for(;;)
  164.    {
  165.       /* Clear the screen */
  166.       od_clr_scr();
  167.  
  168.       /* Display main menu. */
  169.       
  170.       /* First, attempt to display menu from an EZVOTE.ASC/ANS/AVT/RIP file. */
  171.       if((chMenuChoice = od_hotkey_menu("EZVOTE", "VRADPEH", TRUE)) == 0)
  172.       {
  173.          /* If the EZVOTE file could not be displayed, display our own menu. */
  174.          od_printf("`bright red`       EZVote 5.00 - (C) Copyright 1994, Brian Pirie. All Rights Reseved.\n\r");
  175.  
  176.          od_printf("`dark red`");
  177.          if(od_control.user_ansi || od_control.user_avatar)
  178.          {
  179.             od_repeat(196, 79);
  180.          }
  181.          else
  182.          {
  183.             od_repeat('-', 79);
  184.          }
  185.          od_printf("\n\r\n\r\n\r`dark green`");
  186.          od_printf("                        [`bright green`V`dark green`] Vote on a question\n\r\n\r");
  187.          od_printf("                        [`bright green`R`dark green`] View the results of question\n\r\n\r");
  188.  
  189.          /* Display Add New Question option if adding questions is enabled, */
  190.          /* or if the system operator is using the door.                    */
  191.          if(bAllowAdd ||
  192.             strcmp(od_control.sysop_name, od_control.user_name) == 0)
  193.          {
  194.             od_printf("                        [`bright green`A`dark green`] Add a new question\n\r\n\r");
  195.          }
  196.               
  197.          /* If current user is the system operator, add a D function to permit */
  198.          /* deletion of unwanted questions.                                    */
  199.          if(strcmp(od_control.sysop_name, od_control.user_name) == 0)
  200.          {
  201.             od_printf("                        [`bright green`D`dark green`] Delete a question\n\r\n\r");
  202.          }
  203.          od_printf("                        [`bright green`P`dark green`] Page system operator for chat\n\r\n\r");
  204.          od_printf("                        [`bright green`E`dark green`] Exit door and return to the BBS\n\r\n\r");
  205.          od_printf("                        [`bright green`H`dark green`] End call (hangup)\n\r\n\r\n\r");
  206.          od_printf("`bright white`Press the key corresponding to the option of your choice. (%d mins)\n\r`dark green`",
  207.             od_control.user_timelimit);
  208.                                                                                                        \
  209.          /* Get the user's choice from the main menu. This choice may only be */
  210.          /* V, R, A, D, P, E or H.                                            */
  211.          chMenuChoice = od_get_answer("VRADPEH");
  212.       }
  213.  
  214.       /* Perform the appropriate action based on the user's choice */
  215.       switch(chMenuChoice)
  216.       {
  217.          case 'V':
  218.             /* Call EZVote's function to vote on question */
  219.             VoteOnQuestion();
  220.             break;
  221.             
  222.          case 'R':
  223.             /* Call EZVote's function to view the results of voting */
  224.             ViewResults();
  225.             break;
  226.             
  227.          case 'A':
  228.             /* Call EZVote's function to add a new question if door is */
  229.             /* configured to allow the addition of question.           */
  230.             if(bAllowAdd ||
  231.                strcmp(od_control.sysop_name, od_control.user_name) == 0)
  232.             {
  233.                AddQuestion();
  234.             }
  235.             break;
  236.  
  237.          case 'D':                                                                                      \
  238.             /* Call EZVote's funciton to delete an existing question */
  239.             DeleteQuestion();
  240.             break;
  241.             
  242.          case 'P':
  243.             /* If the user pressed P, allow them page the system operator. */
  244.             od_page();
  245.             break;
  246.  
  247.          case 'E':
  248.             /* If the user pressed E, exit door and return to BBS. */
  249.             od_exit(0, FALSE);
  250.             break;
  251.  
  252.          case 'H':
  253.             /* If the user pressed H, ask whether they wish to hangup. */
  254.             od_printf("\n\rAre you sure you wish to hangup? (Y/N) ");
  255.  
  256.             /* Get user's response */
  257.             chYesOrNo = od_get_answer("YN");
  258.  
  259.             /* If user answered yes, exit door and hangup */
  260.             if(chYesOrNo == 'Y')
  261.             {
  262.                od_exit(0, TRUE);
  263.             }
  264.             break;
  265.       }
  266.    }
  267. }
  268.  
  269.  
  270. /* CustomConfigFunction() is called by OpenDoors to process custom */
  271. /* configuration file keywords that EZVote uses.                   */
  272. void CustomConfigFunction(char *pszKeyword, char *pszOptions)
  273. {
  274.    if(stricmp(pszKeyword, "ViewUnanswered") == 0)
  275.    {
  276.       /* If keyword is ViewUnanswered, set local variable based on contents */
  277.       /* of options string.                                                 */
  278.       if(stricmp(pszOptions, "Yes") == 0)
  279.       {
  280.          nViewResultsFrom = QUESTIONS_VOTED_ON | QUESTIONS_NOT_VOTED_ON;
  281.       }
  282.       else if(stricmp(pszOptions, "No") == 0)
  283.       {
  284.          nViewResultsFrom = QUESTIONS_VOTED_ON;
  285.       }
  286.    }
  287.    else if(stricmp(pszKeyword, "AllowAdd") == 0)
  288.    {
  289.       /* If keyword is ViewUnvoted, set local variable based on contents */
  290.       /* of options string.                                              */
  291.       if(stricmp(pszOptions, "Yes") == 0)
  292.       {
  293.          bAllowAdd = TRUE;
  294.       }
  295.       else if(stricmp(pszOptions, "No") == 0)
  296.       {
  297.          bAllowAdd = FALSE;
  298.       }
  299.    }
  300. }
  301.  
  302.  
  303. /* EZVote configures OpenDoors to call the BeforeExitFunction() before    */
  304. /* the door exists for any reason. You can use this function to close any */
  305. /* files or perform any other operations that you wish to have peformed   */
  306. /* before OpenDoors exists for any reason. The od_control.od_before_exit  */
  307. /* variable sets the function to be called before program exit.           */
  308. void BeforeExitFunction(void)
  309. {
  310.    char szLogMessage[80];
  311.    
  312.    /* Write number of messages voted on to log file. */
  313.    sprintf(szLogMessage, "User has voted on %d question(s)",
  314.       nQuestionsVotedOn);
  315.    od_log_write(szLogMessage);
  316. }
  317.  
  318.  
  319. /* EZVote calls the VoteOnQuestion() function when the user chooses the   */
  320. /* vote command from the main menu. This function displays a list of      */
  321. /* available topics, asks for the user's answer to the topic they select, */
  322. /* and display's the results of voting on that topic.                     */
  323. void VoteOnQuestion(void)
  324. {
  325.    int nQuestion;
  326.    int nAnswer;
  327.    tQuestionRecord QuestionRecord;
  328.    char szNewAnswer[ANSWER_STR_SIZE];
  329.    char szUserInput[3];
  330.    FILE *fpFile;
  331.    int nPageLocation = 0;
  332.  
  333.    /* Loop until the user chooses to return to the main menu, or until */
  334.    /* there are no more questions to vote on.                          */
  335.    for(;;)   
  336.    {
  337.       /* Allow the user to choose a question from the list of questions */
  338.       /* that they have not voted on.                                   */
  339.       nQuestion = ChooseQuestion(QUESTIONS_NOT_VOTED_ON,
  340.          "                              Vote On A Question\n\r",
  341.          &nPageLocation);
  342.    
  343.  
  344.       /* If the user did not choose a question, return to main menu. */   
  345.       if(nQuestion == NO_QUESTION)
  346.       {
  347.          return;
  348.       }
  349.  
  350.       /* Read the question chosen by the user. */
  351.       if(!GetQuestion(nQuestion, &QuestionRecord))
  352.       {
  353.          /* If unable to access file, return to main menu. */
  354.          return;
  355.       }
  356.    
  357.       /* Don't allow addition of new answers if maximum number of answers */
  358.       /* have already been added.                                         */
  359.       if(QuestionRecord.nTotalAnswers >= MAX_ANSWERS)
  360.       {
  361.          QuestionRecord.bCanAddAnswers = FALSE;
  362.       }
  363.    
  364.       /* Loop until user makes a valid respose. */
  365.       for(;;)
  366.       {
  367.          /* Display question to user. */
  368.  
  369.          /* Clear the screen. */
  370.          od_clr_scr();
  371.  
  372.          /* Display question itself. */
  373.          od_printf("`bright red`%s\n\r\n\r", QuestionRecord.szQuestion);
  374.  
  375.          /* Loop for each answer to the question. */   
  376.          for(nAnswer = 0; nAnswer < QuestionRecord.nTotalAnswers; ++nAnswer)
  377.          {
  378.             /* Display answer number and answer. */
  379.             od_printf("`bright green`%d. `dark green`%s\n\r",
  380.                nAnswer + 1,
  381.                QuestionRecord.aszAnswer[nAnswer]);
  382.          }
  383.  
  384.          /* Display prompt to user. */
  385.          od_printf("\n\r`bright white`Enter answer number, ");
  386.          if(QuestionRecord.bCanAddAnswers)
  387.          {
  388.             od_printf("[A] to add your own response, ");
  389.          }
  390.          od_printf("[Q] to quit: `dark green`");
  391.    
  392.          /* Get response from user. */
  393.          od_input_str(szUserInput, 2, ' ', 255);
  394.          /* Add a blank line. */      
  395.          od_printf("\n\r");
  396.    
  397.          /* If user entered Q, return to main menu. */
  398.          if(stricmp(szUserInput, "Q") == 0)
  399.          {
  400.             return;
  401.          }
  402.  
  403.          /* If user enetered A, and adding answers is premitted ... */
  404.          else if (stricmp(szUserInput, "A") == 0
  405.             && QuestionRecord.bCanAddAnswers)
  406.          {
  407.             /* ... Prompt for answer from user. */
  408.             od_printf("`bright green`Please enter your new answer:\n\r");
  409.             od_printf("`dark green`[------------------------------]\n\r ");
  410.          
  411.             /* Get string from user. */
  412.             od_input_str(szNewAnswer, ANSWER_STR_SIZE - 1, ' ', 255);
  413.          
  414.             /* Record that user entered a new answer answer. */
  415.             nAnswer = NEW_ANSWER;
  416.  
  417.             /* If user entered a valid answer, then exit loop. */
  418.             if(strlen(szNewAnswer) > 0)
  419.             {
  420.                break;
  421.             }         
  422.          }
  423.  
  424.          /* Otherwise, attempt to get answer number from user. */      
  425.          nAnswer = atoi(szUserInput) - 1;
  426.  
  427.          /* If user input is not a valid answer. */      
  428.          if(nAnswer < 0 || nAnswer >= QuestionRecord.nTotalAnswers)
  429.          {
  430.             /* Display message. */
  431.             od_printf("That is not a valid response.\n\r");
  432.             WaitForEnter();
  433.          }
  434.          else
  435.          {
  436.             /* Otherwise, exit loop. */
  437.             break;
  438.          }
  439.       }
  440.  
  441.       /* Add user's vote to question. */
  442.    
  443.       /* Open question file for exclusive access by this node. */
  444.       fpFile = ExculsiveFileOpen(QUESTION_FILENAME, "r+b");
  445.       if(fpFile == NULL)
  446.       {
  447.          /* If unable to access file, display error and return. */
  448.          od_printf("Unable to access the question file.\n\r");
  449.          WaitForEnter();
  450.          return;
  451.       }
  452.    
  453.       /* Read the answer record from disk, because it may have been changed. */
  454.       /* by another node. */
  455.       fseek(fpFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
  456.       if(fread(&QuestionRecord, sizeof(tQuestionRecord), 1, fpFile) != 1)
  457.       {
  458.          /* If unable to access file, display error and return. */
  459.          fclose(fpFile);
  460.          od_printf("Umable to read from question file.\n\r");
  461.          WaitForEnter();
  462.          return;
  463.       }
  464.    
  465.       /* If user entered their own answer, try to add it to the question. */
  466.       if(nAnswer == NEW_ANSWER)
  467.       {
  468.          /* Check that there is still room for another answer. */
  469.          if(QuestionRecord.nTotalAnswers >= MAX_ANSWERS)
  470.          {
  471.             fclose(fpFile);
  472.             od_printf("Sorry, this question already has the maximum number of answers.\n\r");
  473.             WaitForEnter();
  474.             return;
  475.          }
  476.       
  477.          /* Set answer number to number of new answer. */
  478.          nAnswer = QuestionRecord.nTotalAnswers;
  479.       
  480.          /* Add 1 to total number of answers. */
  481.          ++QuestionRecord.nTotalAnswers;
  482.       
  483.          /* Initialize new answer string and count. */
  484.          strcpy(QuestionRecord.aszAnswer[nAnswer], szNewAnswer);
  485.          QuestionRecord.auVotesForAnswer[nAnswer] = 0;
  486.       }
  487.    
  488.       /* Add user's vote to question. */
  489.       ++QuestionRecord.auVotesForAnswer[nAnswer];
  490.       ++QuestionRecord.uTotalVotes;
  491.    
  492.       /* Write the question record back to the file. */
  493.       fseek(fpFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
  494.       if(fwrite(&QuestionRecord, sizeof(tQuestionRecord), 1, fpFile) != 1)
  495.       {
  496.          /* If unable to access file, display error and return. */
  497.          fclose(fpFile);
  498.          od_printf("Umable to write question to file.\n\r");
  499.          WaitForEnter();
  500.          return;
  501.       }
  502.    
  503.       /* Close the question file to allow access by other nodes. */
  504.       fclose(fpFile);
  505.    
  506.       /* Record that user has voted on this question. */
  507.       CurrentUserRecord.bVotedOnQuestion[nQuestion] = TRUE;
  508.    
  509.       /* Open user file for exclusive access by this node. */
  510.       fpFile = ExculsiveFileOpen(USER_FILENAME, "r+b");
  511.       if(fpFile == NULL)
  512.       {
  513.          /* If unable to access file, display error and return. */
  514.          od_printf("Unable to access the user file.\n\r");
  515.          WaitForEnter();
  516.          return;
  517.       }
  518.  
  519.       /* Update the user's record in the user file. */
  520.       fseek(fpFile, nCurrentUserNumber * sizeof(tUserRecord), SEEK_SET);
  521.       if(fwrite(&CurrentUserRecord, sizeof(tUserRecord), 1, fpFile) != 1)
  522.       {
  523.          /* If unable to access file, display error and return. */
  524.          fclose(fpFile);
  525.          od_printf("Unable to write to user file.\n\r");
  526.          WaitForEnter();
  527.          return;
  528.       }
  529.    
  530.       /* Close the user file to allow access by other nodes. */
  531.       fclose(fpFile);
  532.    
  533.       /* Display the result of voting on this question to the user. */
  534.       DisplayQuestionResult(&QuestionRecord);
  535.  
  536.       /* Add 1 to count of questions that the user has voted on. */
  537.       nQuestionsVotedOn++;
  538.    }
  539. }
  540.  
  541.  
  542. /* The ViewResults function is called when the user chooses the "view    */
  543. /* results" command from the main menu. This function alows the user to  */
  544. /* choose a question from the list of questions, and then displays the   */
  545. /* results of voting on that question.                                   */
  546. void ViewResults(void)
  547. {
  548.    int nChoice;
  549.    tQuestionRecord QuestionRecord;
  550.    int nPageLocation = 0;
  551.  
  552.    /* Loop until user chooses to return to main menu. */
  553.    for(;;)
  554.    {   
  555.       /* Allow the user to choose a question from the list of questions that */
  556.       /* they have already voted on.                                         */
  557.       nChoice = ChooseQuestion(nViewResultsFrom,
  558.          "                                 View Results\n\r", &nPageLocation);
  559.  
  560.       /* If the user did not choose a question, return to main menu. */   
  561.       if(nChoice == NO_QUESTION)
  562.       {
  563.          return;
  564.       }
  565.    
  566.       /* Read the specified question number from the question file. */
  567.       if(!GetQuestion(nChoice, &QuestionRecord))
  568.       {
  569.          return;
  570.       }
  571.    
  572.       /* Display the results for the selected question. */
  573.       DisplayQuestionResult(&QuestionRecord);
  574.    }
  575. }
  576.  
  577.  
  578. /* The GetQuestion function read the record for the specified question */
  579. /* number from the question file.                                      */
  580. int GetQuestion(int nQuestion, tQuestionRecord *pQuestionRecord)
  581. {
  582.    FILE *fpQuestionFile;
  583.  
  584.    /* Open the question file for exculsive access by this node. */
  585.    fpQuestionFile = ExculsiveFileOpen(QUESTION_FILENAME, "r+b");
  586.    if(fpQuestionFile == NULL)
  587.    {
  588.       /* If unable to access file, display error and return. */
  589.       od_printf("Unable to access the question file.\n\r");
  590.       WaitForEnter();
  591.       return(FALSE);
  592.    }
  593.    
  594.    /* Move to location of question in file. */
  595.    fseek(fpQuestionFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
  596.    
  597.    /* Read the question from the file. */
  598.    if(fread(pQuestionRecord, sizeof(tQuestionRecord), 1, fpQuestionFile) != 1)
  599.    {
  600.       /* If unable to access file, display error and return. */
  601.       fclose(fpQuestionFile);
  602.       od_printf("Umable to read from question file.\n\r");
  603.       WaitForEnter();
  604.       return(FALSE);;
  605.    }
  606.    
  607.    /* Close the question file to allow access by other nodes. */
  608.    fclose(fpQuestionFile);
  609.  
  610.    /* Return with success. */
  611.    return(TRUE);
  612. }
  613.  
  614.  
  615. /* The AddQuestion() function is called when the user chooses the "add    */
  616. /* question" option from the main menu. This function allows the user     */
  617. /* to enter a new question, possible responses, and save the question for */
  618. /* other users to vote on.                                                */
  619. void AddQuestion(void)
  620. {
  621.    tQuestionRecord QuestionRecord;
  622.    FILE *fpQuestionFile;
  623.    char szLogMessage[100];
  624.  
  625.    /* Clear the screen. */
  626.    od_clr_scr();
  627.    
  628.    /* Display screen header. */
  629.    od_printf("`bright red`                                Add A Question\n\r");
  630.    od_printf("`dark red`");
  631.    if(od_control.user_ansi || od_control.user_avatar)
  632.    {
  633.       od_repeat(196, 79);
  634.    }
  635.    else
  636.    {
  637.       od_repeat('-', 79);
  638.    }
  639.    od_printf("\n\r\n\r");
  640.    
  641.    /* Obtain quesiton text from the user. */
  642.    od_printf("`bright green`Enter Your Question (blank line cancels)\n\r");
  643.    od_printf("`dark green`[----------------------------------------------------------------------]\n\r ");
  644.    od_input_str(QuestionRecord.szQuestion, QUESTION_STR_SIZE - 1, ' ', 255);
  645.    
  646.    /* If question was empty, then return to main menu. */
  647.    if(strlen(QuestionRecord.szQuestion) == 0)
  648.    {
  649.       return;
  650.    }
  651.    
  652.    /* Display prompt for answers. */
  653.    od_printf("\n\r`bright green`Enter Possible Answers (blank line when done)\n\r");
  654.    od_printf("`dark green`   [------------------------------]\n\r");
  655.    
  656.    /* Loop, getting answers from user. */
  657.    for(QuestionRecord.nTotalAnswers = 0;
  658.        QuestionRecord.nTotalAnswers < MAX_ANSWERS;
  659.        QuestionRecord.nTotalAnswers++)
  660.    {
  661.       /* Display prompt with answer number. */
  662.       od_printf("`bright green`%2d: `dark green`", QuestionRecord.nTotalAnswers + 1);
  663.       
  664.       /* Get string from user. */
  665.       od_input_str(QuestionRecord.aszAnswer[QuestionRecord.nTotalAnswers],
  666.          ANSWER_STR_SIZE - 1, ' ', 255);
  667.          
  668.       /* If string was empty, then exit loop. */
  669.       if(strlen(QuestionRecord.aszAnswer[QuestionRecord.nTotalAnswers]) == 0)
  670.       {
  671.          break;
  672.       }
  673.       
  674.       /* Reset count of votes for this answer to zero. */
  675.       QuestionRecord.auVotesForAnswer[QuestionRecord.nTotalAnswers] = 0;
  676.    }
  677.    
  678.    /* If no answers were supplied, then cancel, returning to main menu. */
  679.    if(QuestionRecord.nTotalAnswers == 0)
  680.    {
  681.       return;
  682.    }
  683.  
  684.    /* Ask whether users should be able to add their own answers. */
  685.    od_printf("\n\r`bright green`Should voters be able to add their own options? (Y/N) `dark green`");
  686.    
  687.    /* Get answer from user. */
  688.    if(od_get_answer("YN") == 'Y')
  689.    {
  690.       /* If user pressed the 'Y' key. */
  691.       od_printf("Yes\n\r\n\r");
  692.       
  693.       /* Record user's response. */
  694.       QuestionRecord.bCanAddAnswers = TRUE;
  695.    }
  696.    else
  697.    {
  698.       /* If user pressed the 'N' key. */
  699.       od_printf("No\n\r\n\r");
  700.  
  701.       /* Record user's response. */
  702.       QuestionRecord.bCanAddAnswers = FALSE;
  703.    }
  704.    
  705.    /* Confirm save of new question. */
  706.    od_printf("`bright green`Do you wish to save this new question? (Y/N) `dark green`");
  707.    
  708.    /* If user does not want to save the question, return to main menu now. */
  709.    if(od_get_answer("YN") == 'N')
  710.    {
  711.       return;
  712.    }
  713.  
  714.    /* Set total number of votes for this question to 0. */   
  715.    QuestionRecord.uTotalVotes = 0;
  716.    
  717.    /* Set creator name and creation time for this question. */
  718.    strcpy(QuestionRecord.szCreatorName, od_control.user_name);
  719.    QuestionRecord.lCreationTime = time(NULL);
  720.    QuestionRecord.bDeleted = FALSE;
  721.    
  722.    /* Open question file for exclusive access by this node. */
  723.    fpQuestionFile = ExculsiveFileOpen(QUESTION_FILENAME, "a+b");
  724.    if(fpQuestionFile == NULL)
  725.    {
  726.       od_printf("Unable to access the question file.\n\r");
  727.       WaitForEnter();
  728.       return;
  729.    }
  730.    
  731.    /* Determine number of records in question file. */
  732.    fseek(fpQuestionFile, 0, SEEK_END);
  733.    
  734.    /* If question file is full, display message and return to main menu */
  735.    /* after closing file.                                               */
  736.    if(ftell(fpQuestionFile) / sizeof(tQuestionRecord) >= MAX_QUESTIONS)
  737.    {
  738.       fclose(fpQuestionFile);
  739.       od_printf("Cannot add another question, EZVote is limisted to %d questions.\n\r", MAX_QUESTIONS);
  740.       WaitForEnter();
  741.       return;
  742.    }
  743.    
  744.    /* Add new question to file. */
  745.    if(fwrite(&QuestionRecord, sizeof(QuestionRecord), 1, fpQuestionFile) != 1)
  746.    {
  747.       fclose(fpQuestionFile);
  748.       od_printf("Umable to write to question file.\n\r");
  749.       WaitForEnter();
  750.       return;
  751.    }
  752.    
  753.    /* Close question file, allowing other nodes to access file. */
  754.    fclose(fpQuestionFile);
  755.  
  756.    /* Record in the logfile that user has added a new question. */
  757.    sprintf(szLogMessage, "User adding questions: %s",
  758.       QuestionRecord.szQuestion);
  759.    od_log_write(szLogMessage);
  760. }
  761.  
  762.  
  763. /* The DeleteQuestion() function is called when the sysop chooses the   */
  764. /* "delete question" option from the main menu. This function displays  */
  765. /* a list of all questions, allowing the sysop to choose a question for */
  766. /* deletion.                                                            */
  767. void DeleteQuestion(void)
  768. {
  769.    int nQuestion;
  770.    tQuestionRecord QuestionRecord;
  771.    FILE *fpFile;
  772.    int nPageLocation = 0;
  773.  
  774.    /* Check that user is system operator. */
  775.    if(strcmp(od_control.user_name, od_control.sysop_name) != 0)
  776.    {
  777.       return;
  778.    }
  779.  
  780.    /* Allow the user to choose a question from the list of all questions. */
  781.    nQuestion = ChooseQuestion(QUESTIONS_NOT_VOTED_ON | QUESTIONS_VOTED_ON,
  782.       "                              Delete A Question\n\r", &nPageLocation);
  783.  
  784.    /* If the user did not choose a question, return to main menu. */   
  785.    if(nQuestion == NO_QUESTION)
  786.    {
  787.       return;
  788.    }
  789.  
  790.    /* Read the question chosen by the user. */
  791.    if(!GetQuestion(nQuestion, &QuestionRecord))
  792.    {
  793.       /* If unable to access file, return to main menu. */
  794.       return;
  795.    }
  796.  
  797.    /* Confirm deletion of this question. */
  798.    od_printf("\n\r`bright green`Are you sure you want to delete the question:\n\r   `dark green`%s\n\r",
  799.       QuestionRecord.szQuestion);
  800.    od_printf("`bright green`[Y]es or [N]o?\n\r`dark green`");
  801.  
  802.    /* If user canceled deletion, return now. */   
  803.    if(od_get_answer("YN") == 'N')
  804.    {
  805.       return;
  806.    }
  807.  
  808.    /* Mark the question as being deleted. */
  809.    QuestionRecord.bDeleted = TRUE;
  810.    
  811.    /* Open question file for exclusive access by this node. */
  812.    fpFile = ExculsiveFileOpen(QUESTION_FILENAME, "r+b");
  813.    if(fpFile == NULL)
  814.    {
  815.       /* If unable to access file, display error and return. */
  816.       od_printf("Unable to access the question file.\n\r");
  817.       WaitForEnter();
  818.       return;
  819.    }
  820.  
  821.    /* Write the question record back to the file. */
  822.    fseek(fpFile, (long)nQuestion * sizeof(tQuestionRecord), SEEK_SET);
  823.    if(fwrite(&QuestionRecord, sizeof(tQuestionRecord), 1, fpFile) != 1)
  824.    {
  825.       /* If unable to access file, display error and return. */
  826.       fclose(fpFile);
  827.       od_printf("Umable to write question to file.\n\r");
  828.       WaitForEnter();
  829.       return;
  830.    }
  831.    
  832.    /* Close the question file to allow access by other nodes. */
  833.    fclose(fpFile);
  834. }
  835.  
  836.  
  837. /* The ChooseQuestion() function provides a list of questions and allows   */
  838. /* the user to choose a particular question, cancel back to the main menu, */ 
  839. /* and page up and down in the list of questions. Depending upon the value */
  840. /* of the nFromWhichQuestions parameter, this function will present a list */
  841. /* of questions that the user has voted on, a list of questions that the   */
  842. /* user has not voted on, or a list of all questions.                      */
  843. int ChooseQuestion(int nFromWhichQuestions, char *pszTitle, int *nLocation)
  844. {
  845.    int nCurrent;
  846.    int nFileQuestion = 0;
  847.    int nPagedToQuestion = *nLocation;
  848.    int nDisplayedQuestion = 0;
  849.    char bVotedOnQuestion;
  850.    char chCurrent;
  851.    tQuestionRecord QuestionRecord;
  852.    FILE *fpQuestionFile;
  853.    static char szQuestionName[MAX_QUESTIONS][QUESTION_STR_SIZE];
  854.    static int nQuestionNumber[MAX_QUESTIONS];
  855.    
  856.    /* Attempt to open question file. */
  857.    fpQuestionFile = ExculsiveFileOpen(QUESTION_FILENAME, "r+b");
  858.  
  859.    /* If unable to open question file, assume that no questions have been */
  860.    /* created.                                                            */
  861.    if(fpQuestionFile == NULL)
  862.    {
  863.       /* Display "no questions yet" message. */
  864.       od_printf("\n\rNo questions have been created so far.\n\r");
  865.       
  866.       /* Wait for user to press enter. */
  867.       WaitForEnter();
  868.       
  869.       /* Indicate that no question has been chosen. */
  870.       return(NO_QUESTION);
  871.    }
  872.    
  873.    /* Loop for every question record in the file. */
  874.    while(fread(&QuestionRecord, sizeof(QuestionRecord), 1, fpQuestionFile) == 1)
  875.    {
  876.       /* Determine whether or not the user has voted on this question. */
  877.       bVotedOnQuestion = CurrentUserRecord.bVotedOnQuestion[nFileQuestion];
  878.       
  879.       /* If this is the kind of question that the user is choosing from */
  880.       /* right now.                                                     */
  881.       if((bVotedOnQuestion && (nFromWhichQuestions & QUESTIONS_VOTED_ON)) ||
  882.          (!bVotedOnQuestion && (nFromWhichQuestions & QUESTIONS_NOT_VOTED_ON)))
  883.       {
  884.          /* If question is not deleted. */
  885.          if(!QuestionRecord.bDeleted)
  886.          {
  887.             /* Add this question to list to be displayed. */
  888.             strcpy(szQuestionName[nDisplayedQuestion],
  889.                QuestionRecord.szQuestion);
  890.             nQuestionNumber[nDisplayedQuestion] = nFileQuestion;
  891.          
  892.             /* Add one to number of questions to be displayed in list. */
  893.             nDisplayedQuestion++;
  894.          }
  895.       }
  896.       
  897.       /* Move to next question in file. */
  898.       ++nFileQuestion;
  899.    }   
  900.    
  901.    /* Close question file to allow other nodes to access the file. */
  902.    fclose(fpQuestionFile);
  903.  
  904.    /* If there are no questions for the user to choose, display an */
  905.    /* appropriate message and return. */
  906.    if(nDisplayedQuestion == 0)
  907.    {
  908.       /* If we were to list all questions. */
  909.       if((nFromWhichQuestions & QUESTIONS_VOTED_ON)
  910.          && (nFromWhichQuestions & QUESTIONS_NOT_VOTED_ON))
  911.       {
  912.          od_printf("\n\rThere are no questions.\n\r");
  913.       }
  914.       /* If we were to list questions that the user has voted on. */
  915.       else if(nFromWhichQuestions & QUESTIONS_VOTED_ON)
  916.       {
  917.          od_printf("\n\rThere are no questions that you have voted on.\n\r");
  918.       }
  919.       /* Otherwise, we were to list questions that use has not voted on. */
  920.       else
  921.       {
  922.          od_printf("\n\rYou have voted on all the questions.\n\r");
  923.       }
  924.       
  925.       /* Wait for user to press enter key. */
  926.       WaitForEnter();
  927.       
  928.       /* Return, indicating that no question was chosen. */
  929.       return(NO_QUESTION);
  930.    }
  931.  
  932.    /* Ensure that initial paged to location is within range. */
  933.    while(nPagedToQuestion >= nDisplayedQuestion)
  934.    {
  935.       nPagedToQuestion -= QUESTION_PAGE_SIZE;
  936.    }
  937.  
  938.    /* Loop, displaying current page of questions, until the user makes a */
  939.    /* choice.                                                            */
  940.    for(;;)
  941.    {
  942.       /* Clear the screen. */
  943.       od_clr_scr();
  944.  
  945.       /* Display header. */
  946.       od_printf("`bright red`");
  947.       od_printf(pszTitle);
  948.       od_printf("`dark red`");
  949.       if(od_control.user_ansi || od_control.user_avatar)
  950.       {
  951.          od_repeat(196, 79);
  952.       }
  953.       else
  954.       {
  955.          od_repeat('-', 79);
  956.       }
  957.       od_printf("\n\r");
  958.    
  959.       /* Display list of questions on this page. */
  960.       for(nCurrent = 0;
  961.          nCurrent < QUESTION_PAGE_SIZE
  962.          && nCurrent < (nDisplayedQuestion - nPagedToQuestion);
  963.          ++nCurrent)
  964.       {
  965.          /* Determine character to display for current line. */
  966.          if(nCurrent < 9)
  967.          {
  968.             chCurrent = '1' + nCurrent;
  969.          }
  970.          else
  971.          {
  972.             chCurrent = 'A' + (nCurrent - 9);
  973.          }
  974.       
  975.          /* Display this question's title. */
  976.          od_printf("`bright green`%c.`dark green`", chCurrent);
  977.          od_printf(" %s\n\r", szQuestionName[nCurrent + nPagedToQuestion]);
  978.       }
  979.  
  980.       /* Display prompt for input. */
  981.       od_printf("\n\r`bright white`[Page %d]  Choose a question or",
  982.          (nPagedToQuestion / QUESTION_PAGE_SIZE) + 1);
  983.       if(nPagedToQuestion < nDisplayedQuestion - QUESTION_PAGE_SIZE)
  984.       {
  985.          od_printf(" [N]ext page,");
  986.       }
  987.       if(nPagedToQuestion > 0)
  988.       {
  989.          od_printf(" [P]revious page,");
  990.       }
  991.       od_printf(" [Q]uit.\n\r");
  992.       
  993.       /* Loop until the user makes a valid choice. */
  994.       for(;;)
  995.       {      
  996.          /* Get input from user */
  997.          chCurrent = toupper(od_get_key(TRUE));
  998.       
  999.          /* Respond to user's input. */
  1000.       
  1001.          /* If user pressed Q key. */
  1002.          if(chCurrent == 'Q')
  1003.          {
  1004.             /* Return without a choosing a question. */
  1005.             return(NO_QUESTION);
  1006.          }
  1007.       
  1008.          /* If user pressed P key. */
  1009.          else if(chCurrent == 'P')
  1010.          {
  1011.             /* If we are not at the first page. */
  1012.             if(nPagedToQuestion > 0)
  1013.             {
  1014.                /* Move paged to location up one page. */
  1015.                nPagedToQuestion -= QUESTION_PAGE_SIZE;
  1016.                
  1017.                /* Exit user input loop to display next page. */
  1018.                break;
  1019.             }
  1020.          }
  1021.       
  1022.          /* If user pressed N key. */
  1023.          else if(chCurrent == 'N')
  1024.          {
  1025.             /* If there is more questions after this page. */
  1026.             if(nPagedToQuestion < nDisplayedQuestion - QUESTION_PAGE_SIZE)
  1027.             {
  1028.                /* Move paged.to location down one page. */
  1029.                nPagedToQuestion += QUESTION_PAGE_SIZE;
  1030.  
  1031.                /* Exit user input loop to display next page. */
  1032.                break;
  1033.             }
  1034.          }
  1035.       
  1036.          /* Otherwise, check whether the user chose a valid question. */
  1037.          else if ((chCurrent >= '1' && chCurrent <= '9')
  1038.             || (chCurrent >= 'A' && chCurrent <= 'H'))
  1039.          {
  1040.             /* Get question number from key pressed. */
  1041.             if(chCurrent >= '1' && chCurrent <= '9')
  1042.             {
  1043.                nCurrent = chCurrent - '1';
  1044.             }
  1045.             else
  1046.             {
  1047.                nCurrent = (chCurrent - 'A') + 9;
  1048.             }
  1049.          
  1050.             /* Add current paged to position to user's choice. */
  1051.             nCurrent += nPagedToQuestion;
  1052.  
  1053.             /* If this is valid question number. */            
  1054.             if(nCurrent < nDisplayedQuestion)
  1055.             {
  1056.                /* Set caller's current question number. */
  1057.                *nLocation = nPagedToQuestion;
  1058.             
  1059.                /* Return actual question number in file. */
  1060.                return(nQuestionNumber[nCurrent]);
  1061.             }
  1062.          }
  1063.       }
  1064.    }
  1065. }
  1066.  
  1067.  
  1068. /* The DisplayQuestionResult() function is called to display the results */
  1069. /* of voting on a paricular question, and is passed the question record  */
  1070. /* of the question. This function is called when the user selects a      */
  1071. /* question using the "view results" option, and is also called after    */
  1072. /* the user has voted on a question, to display the results of voting on */
  1073. /* that question.                                                        */
  1074. void DisplayQuestionResult(tQuestionRecord *pQuestionRecord)
  1075. {
  1076.    int nAnswer;
  1077.    int uPercent;
  1078.  
  1079.    /* Clear the screen. */
  1080.    od_clr_scr();
  1081.  
  1082.    /* Check that there have been votes on this question. */
  1083.    if(pQuestionRecord->uTotalVotes == 0)
  1084.    {
  1085.       /* If there have been no votes for this question, display a message */
  1086.       /* and return.                                                      */
  1087.       od_printf("Nobody has voted on this question yet.\n\r");
  1088.       WaitForEnter();
  1089.       return;
  1090.    }
  1091.  
  1092.    /* Display question itself. */
  1093.    od_printf("`bright red`%s\n\r", pQuestionRecord->szQuestion);
  1094.  
  1095.    /* Display author's name. */
  1096.    od_printf("`dark red`Question created by %s on %s\n\r",
  1097.       pQuestionRecord->szCreatorName,
  1098.       ctime(&pQuestionRecord->lCreationTime));
  1099.    
  1100.    /* Display heading for responses. */
  1101.    od_printf("`bright green`Response                        Votes  Percent  Graph\n\r`dark green`");
  1102.    if(od_control.user_ansi || od_control.user_avatar)
  1103.    {
  1104.       od_repeat(196, 79);
  1105.    }
  1106.    else
  1107.    {
  1108.       od_repeat('-', 79);
  1109.    }
  1110.    od_printf("\n\r");
  1111.  
  1112.    /* Loop for each answer to the question. */   
  1113.    for(nAnswer = 0; nAnswer < pQuestionRecord->nTotalAnswers; ++nAnswer)
  1114.    {
  1115.       /* Determine percent of users who voted for this answer. */
  1116.       uPercent = (pQuestionRecord->auVotesForAnswer[nAnswer] * 100)
  1117.          / pQuestionRecord->uTotalVotes;
  1118.       
  1119.       /* Display answer, total votes and percentage of votes. */
  1120.       od_printf("`dark green`%-30.30s  %-5u  %3u%%     `bright white`",
  1121.          pQuestionRecord->aszAnswer[nAnswer],
  1122.          pQuestionRecord->auVotesForAnswer[nAnswer],
  1123.          uPercent);
  1124.  
  1125.       /* Display a bar graph corresponding to percent of users who voted */
  1126.       /* for this answer.                                                */
  1127.       if(od_control.user_ansi || od_control.user_avatar)
  1128.       {
  1129.          od_repeat(220, (uPercent * 31) / 100);
  1130.       }
  1131.       else
  1132.       {
  1133.          od_repeat('=', (uPercent * 31) / 100);
  1134.       }
  1135.  
  1136.       /* Move to next line. */
  1137.       od_printf("\n\r");
  1138.    }
  1139.    
  1140.    /* Display footer. */
  1141.    od_printf("`dark green`");
  1142.    if(od_control.user_ansi || od_control.user_avatar)
  1143.    {
  1144.       od_repeat(196, 79);
  1145.    }
  1146.    else
  1147.    {
  1148.       od_repeat('-', 79);
  1149.    }
  1150.    od_printf("\n\r");
  1151.    od_printf("`dark green`                         TOTAL: %u\n\r\n\r",
  1152.       pQuestionRecord->uTotalVotes);
  1153.    
  1154.    /* Wait for user to press enter. */
  1155.    WaitForEnter();
  1156. }
  1157.  
  1158.  
  1159. /* The ReadOrAddCurrentUser() function is used by EZVote to search the    */
  1160. /* EZVote user file for the record containing information on the user who */
  1161. /* is currently using the door. If this is the first time that the user   */
  1162. /* has used this door, then their record will not exist in the user file. */
  1163. /* In this case, this function will add a new record for the current      */
  1164. /* user. This function returns TRUE on success, or FALSE on failure.      */
  1165. int ReadOrAddCurrentUser(void)
  1166. {
  1167.    FILE *fpUserFile;
  1168.    int bGotUser = FALSE;
  1169.    int nQuestion;
  1170.  
  1171.    /* Attempt to open the user file for exclusize access by this node.     */
  1172.    /* This function will wait up to the pre-set amount of time (as defined */   
  1173.    /* near the beginning of this file) for access to the user file.        */
  1174.    fpUserFile = ExculsiveFileOpen(USER_FILENAME, "a+b");
  1175.  
  1176.    /* If unable to open user file, return with failure. */   
  1177.    if(fpUserFile == NULL)
  1178.    {
  1179.       return(FALSE);
  1180.    }
  1181.  
  1182.    /* Begin with the current user record number set to 0. */
  1183.    nCurrentUserNumber = 0;
  1184.  
  1185.    /* Loop for each record in the file */
  1186.    while(fread(&CurrentUserRecord, sizeof(tUserRecord), 1, fpUserFile) == 1)
  1187.    {
  1188.       /* If name in record matches the current user name ... */
  1189.       if(strcmp(CurrentUserRecord.szUserName, od_control.user_name) == 0)
  1190.       {
  1191.          /* ... then record that we have found the user's record, */
  1192.          bGotUser = TRUE;
  1193.          
  1194.          /* and exit the loop. */
  1195.          break;
  1196.       }
  1197.  
  1198.       /* Move user record number to next user record. */      
  1199.       nCurrentUserNumber++;
  1200.    }
  1201.  
  1202.    /* If the user was not found in the file, attempt to add them as a */
  1203.    /* new user if the user file is not already full.                  */
  1204.    if(!bGotUser && nCurrentUserNumber < MAX_USERS)
  1205.    {
  1206.       /* Place the user's name in the current user record. */
  1207.       strcpy(CurrentUserRecord.szUserName, od_control.user_name);
  1208.       
  1209.       /* Record that user hasn't voted on any of the questions. */
  1210.       for(nQuestion = 0; nQuestion < MAX_QUESTIONS; ++nQuestion)
  1211.       {
  1212.          CurrentUserRecord.bVotedOnQuestion[nQuestion] = FALSE;
  1213.       }
  1214.       
  1215.       /* Write the new record to the file. */
  1216.       if(fwrite(&CurrentUserRecord, sizeof(tUserRecord), 1, fpUserFile) == 1)
  1217.       {
  1218.          /* If write succeeded, record that we now have a valid user record. */
  1219.          bGotUser = TRUE;
  1220.       }
  1221.    }
  1222.  
  1223.    /* Close the user file to allow other nodes to access it. */
  1224.    fclose(fpUserFile);
  1225.  
  1226.    /* Return, indciating whether or not a valid user record now exists for */
  1227.    /* the user that is currently online.                                   */   
  1228.    return(bGotUser);
  1229. }
  1230.  
  1231.  
  1232. /* The WriteCurrentUser() function is called to save the information on the */
  1233. /* user who is currently using the door, to the EZVOTE.USR file.            */
  1234. void WriteCurrentUser(void)
  1235. {
  1236.    FILE *fpUserFile;
  1237.  
  1238.    /* Attempt to open the user file for exclusize access by this node.     */
  1239.    /* This function will wait up to the pre-set amount of time (as defined */   
  1240.    /* near the beginning of this file) for access to the user file.        */
  1241.    fpUserFile = ExculsiveFileOpen(USER_FILENAME, "r+b");
  1242.  
  1243.    /* If unable to access the user file, display an error message and */
  1244.    /* return.                                                         */
  1245.    if(fpUserFile == NULL)
  1246.    {
  1247.       od_printf("Unable to access the user file.\n\r");
  1248.       WaitForEnter();
  1249.       return;
  1250.    }
  1251.    
  1252.    /* Move to appropriate location in user file for the current user's */
  1253.    /* record. */
  1254.    fseek(fpUserFile, (long)nCurrentUserNumber * sizeof(tUserRecord), SEEK_SET);
  1255.  
  1256.    /* Write the new record to the file. */
  1257.    if(fwrite(&CurrentUserRecord, sizeof(tUserRecord), 1, fpUserFile) == 1)
  1258.    {
  1259.       /* If unable to write the record, display an error message. */
  1260.       fclose(fpUserFile);
  1261.       od_printf("Unable to update your user record file.\n\r");
  1262.       WaitForEnter();
  1263.       return;
  1264.    }
  1265.    
  1266.    /* Close the user file to allow other nodes to access it again. */
  1267.    fclose(fpUserFile);
  1268. }
  1269.  
  1270.  
  1271. /* This function opens the specified file in the specified mode for         */
  1272. /* exculsive access by this node; while the file is open, other nodes will  */
  1273. /* be unable to open the file. This function will wait for up to the number */
  1274. /* of seconds set by FILE_ACCESS_MAX_WAIT, which is defined near the        */
  1275. /* beginning of this file.                                                  */
  1276. FILE *ExculsiveFileOpen(char *pszFileName, char *pszMode)
  1277. {
  1278.    FILE *fpFile = NULL;
  1279.    time_t StartTime = time(NULL);
  1280.  
  1281.    /* Attempt to open the file while there is still time remaining. */    
  1282.    while((fpFile = fopen(pszFileName, pszMode)) == NULL
  1283.       && errno == EACCES
  1284.       && difftime(time(NULL), StartTime) < FILE_ACCESS_MAX_WAIT)
  1285.    {
  1286.       /* If we were unable to open the file, call od_kernal, so that    */
  1287.       /* OpenDoors can continue to respond to sysop function keys, loss */
  1288.       /* of connection, etc.                                            */
  1289.       od_kernal();
  1290.    }
  1291.  
  1292.    /* Return FILE pointer for opened file, if any. */   
  1293.    return(fpFile);
  1294. }
  1295.  
  1296.  
  1297. /* The WaitForEnter() function is used by EZVote to create its custom */
  1298. /* "Press [ENTER] to continue." prompt.                               */
  1299. void WaitForEnter(void)
  1300. {
  1301.    /* Display prompt. */
  1302.    od_printf("`bright white`Press [ENTER] to continue.\n\r");
  1303.    
  1304.    /* Wait for a Carriage Return or Line Feed character from the user. */
  1305.    od_get_answer("\n\r");
  1306. }
  1307.